/**
 * Created 2008.01.31
 */
package bancosys.tec.rtm.impl;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.security.Principal;
import java.util.List;

import javax.security.auth.Subject;

import bancosys.tec.persist.controller.TransactionalController;
import bancosys.tec.rtm.ExceptionContext;
import bancosys.tec.rtm.exception.ExceptionMonitor;
import bancosys.tec.rtm.exception.ExceptionPersister;
import bancosys.tec.rtm.exception.RTMException;
import bancosys.tec.rtm.impl.exception.dao.ExceptionRecordDAO;
import bancosys.tec.rtm.impl.exception.domain.ExceptionRecord;
import bancosys.tec.utils.date.Timestamp;

/**
 * Controller do RTM
 * 
 * @author piercio
 * @author Gustavo Almeida
 */
public class RtmController extends TransactionalController implements ExceptionPersister, jmine.tec.rtm.ExceptionMonitor {
    private ExceptionMonitor exceptionMonitor;

    private String system;

    /**
     * Armazena uma exceção, Criando um registro do tipo exceptionRecord.
     * 
     * @param exception a exceção a ser persistida.
     * @return registro da exceção.
     * @throws RTMException caso não seja possível salvar o registro.
     */
    public ExceptionRecord persist(Throwable exception) throws RTMException {
        ExceptionRecordDAO dao = this.getDAOFactory().getDAOByClass(ExceptionRecordDAO.class);
        ExceptionRecord er = dao.createBean();
        er.setContext(ExceptionContext.getCurrentContext().getEntries());
        er.setTimestamp(new Timestamp());
        er.setSystem(this.system);
        er.setUser(this.getCurrentUser());
        er.setMensagem(exception.getMessage());
        er.setStack(this.getMessageStack(exception) + "\n\n" + this.getStackTraceAsString(exception));
        this.getPersisterFactory().getPersister(this.getSessionFactory(), er).save();
        return er;
    }

    /**
     * Devolve o nome do usuário atual da thread.
     * 
     * @return nome do usuário atual.
     */
    private String getCurrentUser() {
        Subject subject = this.getSecurityService().getCurrentThreadSubject();
        String username = "";
        if (subject != null) {
            Principal principal = subject.getPrincipals().iterator().next();
            username = principal.getName();
        }
        return username;
    }

    /**
     * Busca a lista de mensagens de todas as exceções na stack.
     * 
     * @param exception a exceção.
     * @return lista de mensagens de todas as exceções na stack.
     */
    private String getMessageStack(Throwable exception) {
        String message = "";
        if (exception == null) {
            return message;
        }
        if (exception.getCause() != null) {
            message = this.getMessageStack(exception.getCause());
        }
        if (exception.getMessage() != null && !"".equals(exception.getMessage().trim())) {
            message = exception.getMessage() + "\n" + message;
        }
        return message;

    }

    /**
     * Elimina do banco todos os registros anterior aos dias especificados no parâmetro. Apenas os registros ocorridos entre a data atual
     * menos os dias especificados e a data atual serão mantidos.
     * 
     * @param dias o numero de dias de que os registros serão mantidos.
     * @throws RTMException caso não seja possível remover algum registro.
     */
    public void cleanRTM(int dias) throws RTMException {
        Timestamp data = new Timestamp();
        data.addDays(-1 * dias);

        ExceptionRecordDAO erDAO = this.getDAOFactory().getDAOByClass(ExceptionRecordDAO.class);

        try {
            List<ExceptionRecord> toRemove = erDAO.findTraces(-1, -1, null, data.asCalendar(), null, null, null, null);

            for (ExceptionRecord er : toRemove) {
                this.getPersisterFactory().getPersister(this.getSessionFactory(), er).remove();
            }
        } catch (Exception e) {
            throw new RTMException(e);
        }
    }

    /**
     * Converte o stack trace de uma exceção em String
     * 
     * @param exception a exceção que contém o stack trace a ser convertido.
     * @return uma String contendo o stack trace da exceção
     */
    private String getStackTraceAsString(Throwable exception) {
        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        exception.printStackTrace(printWriter);
        return result.toString();
    }

    /**
     * Define o exceptionMonitor, o exception monitor recebido como parâmetro será inicializado com o finder ({@link ExceptionRecordDAO}) e
     * como o persister (<code>this</code>).
     * 
     * @param exceptionMonitor exception monitor.
     */
    public void setExceptionMonitor(ExceptionMonitor exceptionMonitor) {
        exceptionMonitor.setFinder(this.getDAOFactory().getDAOByClass(ExceptionRecordDAO.class));
        exceptionMonitor.setPersister(this);
        this.exceptionMonitor = exceptionMonitor;
    }

    /**
     * Registra a exception utilizando para isso uma transação isolada.
     * 
     * @param throwable exception a ser registrada.
     */
    public void monitor(Throwable throwable) {
        boolean commit;
        try {
            this.startTransaction();
            commit = true;
        } catch (Throwable e) {
            commit = false;
        }
        try {
            this.exceptionMonitor.monitor(throwable);
        } finally {
            if (commit) {
                this.commit();
            }
        }
    }

    /**
     * @param system the sistema to set
     */
    public void setSystem(String system) {
        this.system = system;
    }

}
